﻿#region Namespace References

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
using DynConDevCorp.OpenArch.SmartData.Interfaces;
using DynConDevCorp.OpenArch.SmartData.Interfaces.Attributes;
using DynConDevCorp.OpenArch.SmartData.Interfaces.MetaData;

#endregion

namespace DynConDevCorp.OpenArch.SmartData.Core.Metadata
{
    public class SmartMetaEntity : ISmartMetaEntity
    {
        #region Constructors

        public SmartMetaEntity(Type entityType)
        {
            m_EntityType = entityType;
            m_AutoFields = GetAutoFields();
        }

        public void DeclareField(string name, Type type)
        {
            SmartMetaAutoField autoField = new SmartMetaAutoField {FieldName = name};
            autoField.ActualType = type;
            m_AutoFields.Add(autoField);
        }

        #endregion

        ////private static void WireEvents(Object targetInstance, string targetMethodName, Type sourceType, String sourceEventName, Object sourceInstance)
        ////{
        ////    SmartMetaEvent metaEvent = new SmartMetaEvent(sourceType, sourceEventName);
        ////    metaEvent.Wire(sourceInstance, targetInstance, targetMethodName);
        ////}

        
        public void Initialize(ISmartEntity entity)
        {
            //try
            //{
                foreach (SmartMetaAutoField metaAutoField in m_AutoFields)
                {
                    if (metaAutoField.AutoCreate)
                    {

                        ISmartField fieldValue = SmartFactory.GlobalInstance.CreateInstance(metaAutoField.ActualType);
                        if (metaAutoField.Property != null)
                        {
                            metaAutoField.Property.SetValue(entity, fieldValue, new object[0]);
                        }
                        entity.RegisterField(metaAutoField.FieldName, fieldValue);
                        fieldValue.InputInhibited = metaAutoField.InputInhibited;
                        if (!String.IsNullOrEmpty(metaAutoField.DisplayFormatString))
                            fieldValue.DisplayFormatString = metaAutoField.DisplayFormatString;
                        if (!String.IsNullOrEmpty(metaAutoField.ValueChangedTargetMethodName))
                        {
                            metaAutoField.ValueChangedEvent.Wire(fieldValue, entity,
                                                                 metaAutoField.ValueChangedTargetMethodName);
                        }
                    }
                }
            //}
            //catch (Exception ex)
            //{
                
            //}
        }

        #region Private Instance Methods

        private static void CalculateType(Type propertyType, out Type rootType, out Type specializationType, out Type actualType)
        {
            if (typeof (ISmartCollection).IsAssignableFrom(propertyType))
            {
                rootType = typeof (SmartEntityCollection<SmartEntity>).GetGenericTypeDefinition();
                specializationType = propertyType.GetGenericArguments()[0];
            }
            else if (typeof(ISmartEntityLink).IsAssignableFrom(propertyType))
            {
                if (propertyType.IsGenericType && !propertyType.IsGenericTypeDefinition)
                {
                    rootType = typeof(SmartEntityLink<SmartEntity>).GetGenericTypeDefinition();
                    specializationType = propertyType.GetGenericArguments()[0];
                }
                else
                {
                    rootType = propertyType;
                    specializationType = null;
                }
            }
            else if (typeof(ISmartEntity).IsAssignableFrom(propertyType))
            {
                rootType = propertyType;
                specializationType = null;
            }
            else if (typeof(ISmartField).IsAssignableFrom(propertyType))
            {
                if (propertyType.IsGenericType && !propertyType.IsGenericTypeDefinition)
                {
                    rootType = typeof(SmartField<int>).GetGenericTypeDefinition();
                    specializationType = propertyType.GetGenericArguments()[0];
                }
                else
                {
                    rootType = propertyType;
                    specializationType = null;
                }
            }
            else
            {
                rootType = null;
                specializationType = null;
            }
            actualType = specializationType != null ? rootType.MakeGenericType(new[] {specializationType}) : rootType;
            if (actualType == null)
            {
                throw new ArrayTypeMismatchException(String.Format(CultureInfo.InvariantCulture, "Could not Determine Type for {0}",
                                                                   propertyType.FullName));
            }
        }

        public static bool Verbose { get; set;  }
        public List<ISmartMetaAutoField> GetAutoFields()
        {
            List<ISmartMetaAutoField> retVal = new List<ISmartMetaAutoField>();
            const BindingFlags flags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy;
            PropertyInfo[] allProperties = m_EntityType.GetProperties(flags);
            foreach (PropertyInfo property in allProperties)
            {
                if (Verbose)
                {
                    //Console.WriteLine("Property: {0} - {1}", m_EntityType.Name, property.Name);
                    if (property.Name == "Item")
                    {
                        int x = 1;
                    }
                }
                object[] attributes = property.GetCustomAttributes(typeof (AutoFieldAttribute), true);
                if (attributes.Length >= 1)
                {
                    if (attributes.Length > 1)
                    {
                        throw new Exception("Multiple Attributes on Property!!!!");
                    }
                    AutoFieldAttribute autoFieldAttribute = (AutoFieldAttribute) attributes[0];
                    //Console.WriteLine("Attributed Property: {0} - {1}", m_EntityType.Name, property.Name);
                    string fieldName = autoFieldAttribute.FieldName;
                    if (String.IsNullOrEmpty(fieldName))
                    {
                        fieldName = property.Name;
                    }
                    Type rootType;
                    Type specializationType;
                    Type actualType;
                    CalculateType(property.PropertyType, out rootType, out specializationType, out actualType);
                    SmartMetaEvent valueChangedEvent = null;
                    if (!String.IsNullOrEmpty(autoFieldAttribute.ValueChanged))
                    {
                        valueChangedEvent = new SmartMetaEvent(actualType, "ValueChanged");
                    }
                    SmartMetaAutoField smartMetaAutoField = new SmartMetaAutoField
                                                                {
                                                                    FieldName = fieldName,
                                                                    Property = property,
                                                                    ConcreteType = rootType,
                                                                    SpecializationType = specializationType,
                                                                    ActualType = actualType,
                                                                    ValueChangedEvent = valueChangedEvent,
                                                                    ValueChangedTargetMethodName =
                                                                        autoFieldAttribute.ValueChanged,
                                                                    AutoCreate = autoFieldAttribute.AutoCreate,
                                                                    InputInhibited = autoFieldAttribute.InputInhibited,
                                                                    DisplayFormatString =
                                                                        autoFieldAttribute.DisplayFormatString
                                                                };
                    retVal.Add(smartMetaAutoField);

                }
             }

            return retVal;
        }

        #endregion

        #region Fields and Constants

        private readonly Type m_EntityType;
        private readonly List<ISmartMetaAutoField> m_AutoFields;

        #endregion
    }
}